﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace PhoneBook
{
    /**
     * Class representing a phone book. 
     * @author Occello Audrey
     * 
     * Add method SearchContact
     * @author Grégory Marro
     */
    public class PhoneBook {

	    private Person owner;
	    private List<Contact> contactList;
	    private Dictionary<String, Group> groupList;
    	
	    /**
	     * Create an empty phone book whose owner is p.
	     * @param p the person owning the phone book.
	     */
	    public PhoneBook(Person p) {
		    owner = p;
		    contactList = new List<Contact>();       
		    groupList = new Dictionary<String, Group>();
	    }
    	
	    /**
	     * Default constructor that creates the phone book of Abraham Simpson (the father of Homer).
	     * Three groups (family, business, friends) are added as well as a set of default contacts.
	     * To use for test purpose.
	     */
	    public PhoneBook() {

		    owner = new Person("Abraham", "Simpson");
    		
		    contactList = new List<Contact>();
		    groupList = new Dictionary<String, Group>();
    		
		    Person tom = new Person("Tom", "Sawyer");
		    Person alice = new Person("Alice", "Wonderland");
		    Person mickey = new Person("Mickey", "Mouse");
		    Person mini = new Person("Mini", "Mouse");
		    Person donald = new Person("Donal", "Duck");
		    Person roger = new Person("Roger", "Rabbit");
		    Person homer = new Person("Homer", "Simpson");
		    Person marge = new Person("Marge", "Simpson");
		    Person bart = new Person("Bart", "Simpson");
		    Person lisa = new Person("Lisa", "Simpson");
		    Person maggie = new Person("Maggie", "Simpson");
    		
		    Contact tomc = new Contact(tom, "", "0400000001", "", "");
		    Contact alicec = new Contact(alice, "", "0400000002", "0600000002", "");
		    Contact mickeyc = new Contact(mickey, "0900000003", "", "0600000003", "");
		    Contact minic = new Contact(mini, "0900000003", "", "0600000004", "");
		    Contact donaldc = new Contact(donald, "0900000005", "", "", "");
		    Contact rogerc = new Contact(roger, "", "", "0600000006", "");
		    Contact homerc = new Contact(homer, "0900000007", "0400000007", "0600000007", "");
		    Contact margec = new Contact(marge, "0900000007", "", "0600000008", "");
		    Contact bartc = new Contact(bart, "0900000007", "", "0600000009", "");
		    Contact lisac = new Contact(lisa, "0900000007", "", "0600000010", "");
		    Contact maggiec = new Contact(maggie, "0900000007", "", "", "");
    		
		    contactList.Add(tomc);
		    contactList.Add(alicec);
		    contactList.Add(mickeyc);
		    contactList.Add(minic);
		    contactList.Add(donaldc); 
		    contactList.Add(rogerc);
		    contactList.Add(homerc);
		    contactList.Add(margec);
		    contactList.Add(bartc);
		    contactList.Add(lisac);
		    contactList.Add(maggiec);
    		
		    Group family = new Group("Family");
		    Group friends = new Group("Friends");
		    Group business = new Group("Business");
    		
		    groupList.Add(family.getGroupName(), family);
		    groupList.Add(friends.getGroupName(), friends);
		    groupList.Add(business.getGroupName(), business);
    		
		    business.addToGroup(tomc);
		    business.addToGroup(alicec);
		    friends.addToGroup(donaldc);
		    friends.addToGroup(rogerc);
		    friends.addToGroup(mickeyc);
		    friends.addToGroup(minic);
		    family.addToGroup(homerc);
		    family.addToGroup(margec);
		    family.addToGroup(bartc);
		    family.addToGroup(lisac);
		    family.addToGroup(maggiec);
	    }
    	
	    /**
	     * Retrieve the owner identity of the phone book.
	     * @return the owner.
	     */
	    public Person phoneBookOwner() {
		    return owner;
	    }
    	
	    /**
	     * Add a new contact to the phone book.
	     * An exception is thrown if the contact already exists.
	     * @param c the contact to be added.
	     * @throws ContactAlreadyDefinedException
	     */
	    public void addContact(Contact c) {
		    if (contactList.Contains(c)) throw new ContactAlreadyDefinedException("Contact info for "+c.getPerson()+ " already defined!");
		    contactList.Add(c);
	    }
	    /** 
	     * Delete a contact from the phone book.
	     * An exception is thrown if attempting to remove a contact that does not exists.
	     * @param c the contact to be deleted.
	     * @throws ContactNotDefinedException
	     */
	    public void deleteContact(Contact c) {
		    if (!contactList.Contains(c)) throw new ContactNotDefinedException("Contact "+c.getPerson()+ " not defined!");		    
            foreach(Group g in groupList.Values)
            {
		        g.removeFromGroup(c);
            }
		    contactList.Remove(c);
	    }
	    /**
	     * 
	     * @param c the old content of the contact
	     * @param newc the new content of the contact
	     * @throws ContactNotDefinedException
	     * @throws ContactAlreadyDefinedException
	     * @throws ContactUpdateException
	     */
	    public void modifyContact(Contact c, Contact newc)  {
		    if (!c.getPerson().Equals(newc.getPerson())) throw new ContactUpdateException("The firstname and lastname of a contact cannot be changed!");
		    
            // We have to record the group of this contact before deleting it
            Group g = getGroupByContact(c);
            deleteContact(c);
		    addContact(newc);

            if (g != null)
            {
                g.addToGroup(newc);
            }
	    }
    	
	    /**
	     * Retrieve all the contacts of the phone book.
	     * @return the list of contacts.
	     */
	    public List<Contact> getAllContacts() {
		    return contactList;
	    }
    	
	    /**
	     * Retrieve all the contact identities (Person) of the phone book.
	     * @param contacts is the list of contacts with phone number details.
	     * @return the list of persons.
	     */
	    public List<Person> getContactsWithoutDetails(List<Contact> contacts) {	
	        List<Person> cp = new List<Person>();
            foreach(Contact c in contacts)
            {
                cp.Add(c.getPerson());
            }
		    return cp;
	    }
    	
	    /**
	     * Retrieve all the group names.
	     * @return the list of existing group names.
	     */
	    public List<String> getGroupNames() {
            List<String> list = new List<String>();
            foreach (String str in groupList.Keys)
            {
                list.Add(str);
            }
            return list;
	    }
    	
	    /**
	     * Retrieve the contacts belonging to a given group.
	     * An exception is thrown if the group does not exists.
	     * @param groupname the name of the group whose contacts will be retrieved.
	     * @return the list of contacts associated with the given group.
	     * @throws GroupNotDefinedException
	     */
	    public List<Contact> getContactsByGroup(String groupname) {
		    if (! groupList.ContainsKey(groupname)) throw new GroupNotDefinedException(groupname+" does not correspond to a group");
            if (groupList.ContainsKey(groupname))
            {
                return groupList[groupname].getGroupList();
            }
            return new List<Contact>();
	    }
    	
	    /**
	     * Method which consists to look for a contact from a string parameter 
	     * @param str
	     * @return List<Contact>
	     */
	    public List<Contact> searchContact(String str)
	    {
		    List<Contact> contactsToReturn = new List<Contact>();
		    foreach (Contact contact in contactList)
		    {
			    if (contact.getPerson().getFirstname().Contains(str) || contact.getPerson().getLastname().Contains(str))
			    {
				    contactsToReturn.Add(contact);
			    }
		    }
		    return contactsToReturn;
	    }
    	
	    /**
	     * Method which consists to look for a contact from a string parameter
	     * and the name of a group
	     * @param str
	     * @param group
	     * @return List<Contact>
	     */
	    public List<Contact> searchContact(String str, Group group)
	    {
		    List<Contact> contactsToReturn = new List<Contact>();

		    foreach (Contact contact in group.getGroupList())
		    {
			    if (contact.getPerson().getFirstname().Contains(str) || contact.getPerson().getLastname().Contains(str))
			    {
				    contactsToReturn.Add(contact);
			    }
		    }
		    return contactsToReturn;
	    }

        /**
         * Method which consists to return all groups on the phonebook
         * @return List<Group>
         **/
        public List<Group> getAllGroups()
        {
            List<Group> list = new List<Group>();
            foreach (Group g in groupList.Values)
            {
                list.Add(g);
            }
            return list;
        }

        /**
         * Get the group of a contact
         * @param contact
         * @return Group
         **/
        public Group getGroupByContact(Contact c)
        {
            foreach (Group g in groupList.Values)
            {
                foreach (Contact contact in g.getGroupList())
                {
                    if (c.getPerson().getFirstname().Equals(contact.getPerson().getFirstname()) && c.getPerson().getLastname().Equals(contact.getPerson().getLastname()))
                    {
                        return g;
                    }
                }
            }
            return null;
        }
    }
}